#include <54func.h>
int main(int argc,char *argv[])
{
    // ./5_azhen 192.168.187,128 1234
    ARGS_CHECK(argc,3);
    //创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    //初始化要绑定的IP地址：
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;//地址族为IPv4
    serverAddr.sin_port =htons(atoi(argv[2]));//将指定的端口先转为整数->转为网络字节序
    serverAddr.sin_addr.s_addr =inet_addr(argv[1]);//将点分十进制转换为网络地址
    int reuse =1;
    int ret = setsockopt(sockfd,SOL_SOCKET,
                         SO_REUSEADDR,&reuse,sizeof(reuse));
    //绑定套接字：将套接字绑定到指定的IP地址和端口上
    ret = bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
    ERROR_CHECK(ret,-1,"bind");
    //监听连接：将套接字设置为监听模式，最多可以有X个未完成的连接请求在队列中等待，X主观设置
    listen(sockfd,50);//此刻服务器被动，但是能够接收来自客户端的连接请求
    //服务器创建新的套接字与客户端进行通信
    int netfd = accept(sockfd,NULL,NULL);//这里就不获取客户端的ip地址以及端口
    printf("aqiang has connect!\n");
    //利用select进行即使通信
    char buf[4096] = {0};
   
    // fd_set rdset;
    int epfd = epoll_create(1); //再内核中创建epoll文件对象，包含一个监听集合和一个就绪集合
    
    //设置监听集合
    struct epoll_event event;//1、说明就绪的条件（读就绪还是写就绪）  2、就绪的行为（文件描述符）
    event.events = EPOLLIN;//读就绪
    event.data.fd = STDIN_FILENO;
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&event);
    event.events = EPOLLIN;
    event.data.fd = netfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,netfd,&event);

    while(1)
    {
       // //select之前需要重置监听集合
       // FD_ZERO(&rdset);
       // //把引起阻塞的行为加入到监听集合中
       // FD_SET(STDIN_FILENO,&rdset);
       // FD_SET(netfd,&rdset);
       // select(netfd+1,&rdset,NULL,NULL,NULL);
       

       // //select返回，说明stdin or netfd就绪了，
       // epoll_wait会将内核态的就绪集合拷贝到用户态的，并且这是个数组
       struct epoll_event readySet[2];//就绪集合，这里最多就两个
       int readyNum = epoll_wait(epfd,readySet,2,-1);//-1表示无限等待
        
       //循环遍历就绪数组
       //阻塞：当没有任何I/O事件发生时，epoll_wait会阻塞进程，不会占用CPU资源
       //唤醒：当某个文件描述符尊卑好，内核会通知epoll实例，epoll_wait会返回就绪事件的数量，打破阻塞
       for(int i = 0;i<readyNum;i++)
       {
           //如果键盘操作就绪
            if(readySet[i].data.fd == STDIN_FILENO)
            {
                //发消息给客户端
                bzero(buf,sizeof(buf));
                ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));
                if(sret == 0)
                {
                    printf("azhen主动断开！");
                    break;
                }
                send(netfd,buf,strlen(buf),0);

            }else if(readySet[i].data.fd == netfd)
            {
                //接收消息
                bzero(buf,sizeof(buf));
                ssize_t sret = recv(netfd,buf,sizeof(buf),0);
                if(sret == 0)
                {
                    printf("aqiang主动断开！\n");
                    break;
                }
                printf("aqiang：%s\n",buf);
            }
       }

       // if(FD_ISSET(STDIN_FILENO,&rdset))
       // {
       //     //读取stdin的数据
       //     bzero(buf,sizeof(buf));
       //     ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));//read功能很强大，可以从磁盘，标准输入中区读取数据
       //     if(sret == 0)//成功时，返回读取的字节数（0表示文件结束）
       //     {
       //         printf("aqiang is leaver!\n");
       //         break;
       //     }
       //     send(netfd,buf,strlen(buf),0);     //send本质上是write，send只能操作socket
       // }
       // if(FD_ISSET(netfd,&rdset))
       // {
       //     //读取套接字的内容
       //     bzero(buf,sizeof(buf));
       //     ssize_t sret = recv(netfd,buf,sizeof(buf),0);//recv本质上就是read，但是recv只能操作socket
       //     
       //     //如果网络的另外一端断开连接，那么我这个从网络读取的这一端会一直读取0
       //     if(sret == 0)
       //     {
       //         printf("aqiang已经断开！\n");
       //         break;
       //     }
       //     printf("aqiang：%s\n",buf);
       // }
    }
    return 0;
}
